/*-
 * SMSSubmit.java
 *
 * $LastChangedBy: stein $
 * $LastChangedRevision: 98 $
 * $LastChangedDate: 2007-02-20 15:18:26 +0000 (Tue, 20 Feb 2007) $
 * $HeadURL: svn://localhost/SMS/src/com/googlecode/attention/sms/pdu/SMSSubmit.java $
 *
 * Copyright (c) 2007, Stein Gunnar Bakkeby
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Stein Gunnar Bakkeby nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.googlecode.attention.sms.pdu;

import java.util.EnumSet;

import com.googlecode.attention.util.IntArrayBuilder;
import com.googlecode.attention.util.KeepingReference;

import static com.googlecode.attention.sms.pdu.PDUElement.*;

/** * SMS-SUBMIT. * <BR>Basic elements of the SMS-SUBMIT type: * <BR> * <BR> * <TABLE BORDER="1"> * <TR><TD>Abbr.</TD><TD>Reference</TD><TD>P1)</TD><TD>P2)</TD><TD>Description</TD></TR> * <TR><TD>TP-MTI</TD><TD>TP-Message-Type-Indicator</TD><TD>M</TD><TD>2b</TD><TD>Parameter describing the message type.</TD></TR> * <TR><TD>TP-RD</TD><TD>TP-Reject-Duplicates</TD><TD>M</TD><TD>b</TD><TD>Parameter indicating whether or not the SC shall accept an SMS-SUBMIT for an SM still held in the SC which has the same TP-MR and the same TP-DA as a previously submitted SM from the same OA</TD></TR> * <TR><TD>TP-VPF</TD><TD>TP-Validity-Period-Format</TD><TD>M</TD><TD>2b</TD><TD>Parameter indicating whether or not the TP-VP field is present.</TD></TR> * <TR><TD>TP-RP</TD><TD>TP-Reply-Path</TD><TD>M</TD><TD>b</TD><TD>Parameter indicating the request for Reply Path.</TD></TR> * <TR><TD>TP-UDHI</TD><TD>TP-User-Data-Header-Indicator</TD><TD>O</TD><TD>b</TD><TD>Parameter indicating that the TP-UD field contains a Header.</TD></TR> * <TR><TD>TP-SRR</TD><TD>TP-Status-Report-Request</TD><TD>O</TD><TD>b</TD><TD>Parameter indicating if the MS is requesting a status report.</TD></TR> * <TR><TD>TP-MR</TD><TD>TP-Message-Reference</TD><TD>M</TD><TD>I</TD><TD>Parameter identifying the SMS-SUBMIT.</TD></TR> * <TR><TD>TP-DA</TD><TD>TP-Destination-Address</TD><TD>M</TD><TD>2-12o</TD><TD>Address of the destination SME.</TD></TR> * <TR><TD>TP-PID</TD><TD>TP-Protocol-Identifier</TD><TD>M</TD><TD>o</TD><TD>Parameter identifying the above layer protocol, if any.</TD></TR> * <TR><TD>TP-DCS</TD><TD>TP-Data-Coding-Scheme</TD><TD>M</TD><TD>o</TD><TD>Parameter identifying the coding scheme within the TP-User-Data.</TD></TR> * <TR><TD>TP-VP</TD><TD>TP-Validity-Period</TD><TD>O</TD><TD>o/7o</TD><TD>Parameter identifying the time from where the message is no longer valid.</TD></TR> * <TR><TD>TP-UDL</TD><TD>TP-User-Data-Length</TD><TD>M</TD><TD>I</TD><TD>Parameter indicating the length of the TP-User-Data field to follow.</TD></TR> * <TR><TD>TP-UD</TD><TD>TP-User-Data</TD><TD>O</TD><TD>3)</TD><TD></TD></TR> * </TABLE> * <BR> * <BR>1) Provision; Mandatory (M) or Optional (O). * <BR>2) Representation; Integer (I), bit (b), 2 bits (2b), Octet (o), 7 octets (7o), 2-12 octets (2-12o). * <BR>3) Dependent on the TP-DCS. *  * @author Stein Gunnar Bakkeby */
public class SMSSubmit extends TPDU {
  
  /* Field values */
  private int fo = 1;
  private int mr = 0;
  private Address da = null;
  private int pid = 0;
  private int dcs = 0;
  private VP vp = null;
  
  private final EnumSet<PDUElement> pduElementSet = EnumSet.of(TP_FO, TP_MTI,
      TP_RD, TP_VPF, TP_RP, TP_UDHI, TP_SRR, TP_MR, TP_DA, TP_TOA, TP_NPI,
      TP_TON,TP_PID, TP_DCS, TP_VP, TP_UDL, TP_UD, TP_UDH);
  
  /**
   * Default constructor, creates an empty PDU.
   */
  public SMSSubmit() {
    super(new int[13], TPDU.SMS_SUBMIT);
  }
  
  /**
   * Constructor, creates the SMS-SUBMIT based upon the given PDU.
   * 
   * @param tpdu The raw data
   */
  public SMSSubmit(final String tpdu) {
    super(tpdu, TPDU.SMS_SUBMIT);
  }
  
  /**
   * Constructor, creates the SMS-SUBMIT based upon the given PDU.
   * 
   * @param tpdu The raw data
   */
  public SMSSubmit(final int[] tpdu) {
    super(tpdu, TPDU.SMS_SUBMIT);
  }
  
  /**
   * Constructor, creates the SMS-SUBMIT.
   * 
   * @param fo The first octet
   * @param mr The message reference
   * @param da The destination address
   * @param pid The Protocol-Identifier
   * @param dcs The Data-Coding-Scheme
   * @param vp The Validity-Period
   * @param ud The User-Data
   */
  public SMSSubmit(final FO fo,
                   final MR mr,
                   final Address da,
                   final PID pid,
                   final DCS dcs,
                   final VP vp,
                   final UD ud) {
    this();
    this.fo  = fo.getValue();
    this.mr  = mr.getValue();
    this.da  = da;
    this.pid = pid.getValue();
    this.dcs = dcs.getValue();
    this.vp  = vp;
    this.ud  = ud;
  }
  
  /**
   * Constructor, creates the SMS-SUBMIT without the optional Validity-Period.
   * 
   * @param fo The first octet
   * @param mr The message reference
   * @param da The destination address
   * @param pid The Protocol-Identifier
   * @param dcs The Data-Coding-Scheme
   * @param ud The User-Data
   */
  public SMSSubmit(final FO fo,
                   final MR mr,
                   final Address da,
                   final PID pid,
                   final DCS dcs,
                   final UD ud) {
    this();
    this.fo  = fo.getValue();
    this.mr  = mr.getValue();
    this.da  = da;
    this.pid = pid.getValue();
    this.dcs = dcs.getValue();
    this.ud  = ud;
  }
  
  /**
   * Constructor, creates the SMS-SUBMIT without the optional User-Data.
   * 
   * @param fo The first octet
   * @param mr The message reference
   * @param da The destination address
   * @param pid The Protocol-Identifier
   * @param dcs The Data-Coding-Scheme
   * @param vp The Validity-Period
   */
  public SMSSubmit(final FO fo,
                   final MR mr,
                   final Address da,
                   final PID pid,
                   final DCS dcs,
                   final VP vp) {
    this();
    this.fo  = fo.getValue();
    this.mr  = mr.getValue();
    this.da  = da;
    this.pid = pid.getValue();
    this.dcs = dcs.getValue();
    this.vp  = vp;
  }
  
  /**
   * Constructor, creates the SMS-SUBMIT without the optional User-Data
   * and the optional Validity-Period.
   * 
   * @param fo The first octet
   * @param mr The message reference
   * @param da The destination address
   * @param pid The Protocol-Identifier
   * @param dcs The Data-Coding-Scheme
   */
  public SMSSubmit(final FO fo,
                   final MR mr,
                   final Address da,
                   final PID pid,
                   final DCS dcs) {
    this();
    this.fo  = fo.getValue();
    this.mr  = mr.getValue();
    this.da  = da;
    this.pid = pid.getValue();
    this.dcs = dcs.getValue();
  }
  
  /**
   * This will decode the PDU.
   */
  public void decode() {
    resetPDUPointer();
    fo  = extractOctet();
    // TODO reply path?
    mr  = extractOctet(); // Message-Reference
    final int dal = extractOctet();
    final int toa = extractOctet();
    da  = new Address(dal, new TOA(toa), extractOctets(((dal & 1) == 1 ? dal + 1 : dal) / 2));
    pid = extractOctet();
    dcs = extractOctet();
    /* VP */
    final VPF vpf = VPF.parse(fo);
    switch (vpf) {
    case FIELD_NOT_PRESENT:
      break;
      
    case ENHANCED_FORMAT:
      vp = new VPEnhancedTime(extractOctets(7), vpf);
      break;
      
    case ABSOLUTE_FORMAT:
      vp = new SCTS(extractOctets(7), vpf);
      break;
      
    case RELATIVE_FORMAT:
      vp = new VPRelativeTime(extractOctet(), vpf);
      break;
      
    default:
      break;
    }
    
    final int udl = extractOctet();
    int octetsToExtract = udl;
    final DCS d = new DCS(dcs);
    switch (d.getGroup()) {
    case DCS.GROUP_MESSAGE_WAITING_STORE_UCS2:
      break;
      
    case DCS.GROUP_GENERAL_DATA_CODING:
    case DCS.GROUP_AUTOMATIC_DELETION:
    case DCS.GROUP_MESSAGE_CODING_CLASS:
      switch (d.getMessageCoding()) {
      case DCS.MESSAGE_CODING_8_BIT_DATA:
      case DCS.MESSAGE_CODING_UCS2:
        break;
      
      case DCS.MESSAGE_CODING_RESERVED: // assumed 7 bit default
      case DCS.MESSAGE_CODING_7_BIT_DEFAULT:
      default:
        /* Falls through */
      }
      /* Falls through */
      
    case DCS.GROUP_RESERVED_1000: /* All reserved groups */ 
    case DCS.GROUP_RESERVED_1001: /* are assumed to      */
    case DCS.GROUP_RESERVED_1010: /* use the GSM 7-bit   */
    case DCS.GROUP_RESERVED_1011: /* default alphabet.   */
    case DCS.GROUP_MESSAGE_WAITING_DISCARD:
    case DCS.GROUP_MESSAGE_WAITING_STORE:
    default:
      double res = (double) udl * 7 / 8;
      octetsToExtract = (int) res + (res > (int) res ? 1 : 0);
      break;
    }

    ud = new UD(extractOctets(octetsToExtract),   // UD
                udl,                              // UDL
                UDHI.parse(fo),                   // UDHI
                new DCS(dcs));                    // DCS
  }
  
  /**
   * This will encode the PDU.
   */
  public void encode() {
    /* Avoid NullPointerException */
    if (da == null) da  = new Address();
    if (vp == null) vp  = new VP();
    if (ud == null) ud  = new UD(new int[0], 0, UDHI.parse(fo), new DCS(dcs));
    
    final IntArrayBuilder output = new IntArrayBuilder();
    output.append(fo);
    output.append(mr);
    output.append(da.getAddressField());
    output.append(pid);
    output.append(dcs);
    
    switch (VPF.parse(fo)) {
    case FIELD_NOT_PRESENT:
      break;
      
    case ENHANCED_FORMAT:
    case ABSOLUTE_FORMAT:
    case RELATIVE_FORMAT:
      output.append(vp.getVPOctets());
      break;
      
    default:
      break;
    }

    final int[] enc = ud.getEncoded();
    output.append(ud.getUDL());
    output.append(enc);
    
    if (rest != null && rest.length != 0) {
      output.append(rest);
    }
    this.pdu = output;
  }
  
  /**
   * This will return true if the PDU contains the given type.
   *
   * @param type PDUElement values
   * @see PDUElement
   * @return True if given type is present, false otherwise
   */
  public boolean contains(final PDUElement type) {
    return pduElementSet.contains(type);
  }
  
  
  /**
   * This will return an element of a given type.
   * 
   * @param type PDUElement values
   * @see PDUElement
   * @return The element in this TPDU
   */
  public int get(final PDUElement type) {
    switch (type) {
    case TP_FO:   return fo;
    case TP_MTI:  return fo & TP_MTI.value;
    case TP_RD:   return fo & TP_RD.value;
    case TP_RP:   return fo & TP_RP.value;
    case TP_UDHI: return fo & TP_UDHI.value;
    case TP_SRR:  return fo & TP_SRR.value;
    case TP_TOA:  return (da == null ? 0 : da.getTOA().getValue());
    case TP_TON:  return (da == null ? 0 : da.getTOA().getTypeOfNumber());
    case TP_NPI:  return (da == null ? 0 : da.getTOA().getNumPlanIdent());
    case TP_PID:  return pid;
    case TP_DCS:  return dcs;
    case TP_UDL:  return ud.getUDL();
    case TP_MR:   return mr;
    case TP_VPF:  return vp.getVPF().value;

    default:
      return -1;
    }
  }
  
  /**
   * This will return the destination address.
   * 
   * @return The TP-DA
   */
  public Address getDestinationAddress() {
    return da;
  }
  
  /**
   * This will return the validity period.
   * 
   * @return The TP-VP
   */
  public VP getValidityPeriod() {
    return vp;
  }
  
  /**
   * This will set an element of a given type.
   * 
   * @param type PDUElement values
   * @see PDUElement
   * @param value The value of the element to set
   * @return True if PDU contains the given type, false otherwise
   */
  public boolean set(final PDUElement type, final int value) {
    switch (type) {
    case TP_FO:
      fo = value;
      break;
      
    case TP_PID:
      pid = value;
      break;
      
    case TP_DCS:
      dcs = value;
      break;
      
    case TP_MR:
      mr = value;
      break;
      
    case TP_VPF:
      vp.setVPF(VPF.parse(value));
      break;
      
    case TP_MTI:
    case TP_UDHI:
    case TP_RD:
    case TP_RP:
    case TP_SRR:
      final FO f = new FO(fo);
      f.set(type, value);
      fo = f.getValue();
      break;
    
    default:
      return false;
    }
    return true;
  }
  
  /**
   * This will set the validity period.
   * 
   * @param validityPeriod The VP of this SMS submit.
   */
  @KeepingReference
  public void setValidityPeriod(final VP validityPeriod) {
    this.vp = validityPeriod;
  }
  
  /**
   * This will set the destination address.
   * 
   * @param address The DA of this SMS submit.
   */
  @KeepingReference
  public void setDestinationAddress(final Address address) {
    this.da = address;
  }
  
  /**
   * This will return the destination address. The method is equivalent of
   * the getDestinationAddress() method.
   * 
   * @return TP-DA
   * @see #getDestinationAddress()
   */
  @Override
  public Address getAddress() {
    return getDestinationAddress();
  }
  
  /**
   * This will set the destination address. The method is equivalent of
   * the setDestinationAddress() method.
   * 
   * @see #setDestinationAddress(Address)
   */
  @Override
  public void setAddress(final Address address) {
    setDestinationAddress(address); 
  }
  
  /**
   * @return [FO:&lt;fo&gt;][MR:&lt;mr&gt;][DA:&lt;address&gt;][PID:&lt;pid&gt;][DCS:&lt;dcs&gt;][VP:&lt;vp&gt;][UD:&lt;ud&gt;]
   */
  public String toString() {
    this.decode();
    final StringBuilder sb = new StringBuilder(256);
    sb.append("[FO:");
    sb.append(fo); // TODO fix fo
    sb.append("][MR:");
    sb.append(mr);
    sb.append("][DA:");
    sb.append(da);
    sb.append("][PID:"); // TODO fix pid    
    sb.append(pid);
    sb.append("][DCS:"); // TODO fix dcs
    sb.append(dcs);
    sb.append("][VP:");
    sb.append(vp); // TODO fix vp
    sb.append("][UD:");
    sb.append(ud);
    sb.append(']');
    return sb.toString();
  }
}